package tsif.tcluster.loader;

import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 作用域类加载器<br>
 * 
 */
public class ScopeClassLoader extends URLClassLoader {
	protected String name;
	protected ConcurrentMap<String, Class<?>> classMap;
	protected IClassFilter filter;

	public ScopeClassLoader(String name) {
		this(name, null);
	}

	public ScopeClassLoader(String name, IClassFilter filter) {
		this(name, ((URLClassLoader) ScopeClassLoader.class.getClassLoader()), filter);
	}

	public ScopeClassLoader(String name, URLClassLoader parent, IClassFilter filter) {
		super(parent.getURLs(), parent);
		this.name = name;
		classMap = new ConcurrentHashMap<>();
		this.filter = (filter != null) ? filter : new ParentClassFilter();
	}

	/** 用自身加载类 **/
	public Class<?> loadClassBySelf(String className, boolean resolve) throws ClassNotFoundException {

		synchronized (getClassLoadingLock(className)) {
			// 通过自身加载
			Class<?> c = findLoadedClass(className); // 从已经加载类中尝试获取
			if (c == null) {
				try {
					c = findClass(className); // 直接加载类
				} catch (ClassNotFoundException e) {
					// 无法加载类
				}
			}

			// 加载失败, 父类加载.
			if (c == null) {
				return loadClassByLoader(getParent(), className, resolve); // 转成父类加载(自己不行就靠老爹)
				// return getParent().loadClass(className);
			}

			// // 最后检测
			// if (c == null) {
			// throw new ClassNotFoundException(className);
			// }

			// 链接指定的类
			if (resolve) {
				resolveClass(c);
			}
			return c;
		}
	}

	@Override
	public Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
		// 判断是否交由自身加载
		boolean localLoad = true;
		if (filter != null) {
			localLoad = filter.filter(this, className);
		}
		// System.out.println("load filter: " + className + " " + localLoad);

		// 判断处理, 交给父类
		if (!localLoad) {
			return loadClassByLoader(getParent(), className, resolve);
		}
		// 自身加载
		return loadClassBySelf(className, resolve);
	}

	@Override
	public String toString() {
		return "ScopeLoader[" + name + "]";
	}

	/********************** 静态 ***********************/

	/** load and exec **/
	public static Object exec(ClassLoader loader, String className, String funcName, Object... args) throws Exception {
		Class<?> clazz = loader.loadClass(className);
		Object obj = clazz.newInstance();
		// find method
		Method findMethod = null;
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			if (!method.getName().equals(funcName)) {
				continue;
			}
			findMethod = method;
			break;
		}
		// check method
		if (findMethod == null) {
			throw new java.lang.NoSuchMethodException(funcName);
		}
		// invoke
		return findMethod.invoke(obj, args);
	}

	/** 判断加载器是否加载过类 **/
	protected static Class<?> loadClassByLoader(ClassLoader classLoader, String className, boolean resolve) throws ClassNotFoundException {
		try {
			return (Class<?>) invoke(classLoader, ClassLoader.class, "loadClass", new Class<?>[] { String.class, boolean.class }, className, resolve);
		} catch (Exception e) {
			throw new ClassNotFoundException(className);
		}
	}

	/** 判断加载器是否加载过类 **/
	protected static Class<?> findLoadedClassByLoader(ClassLoader classLoader, String className) {
		return (Class<?>) invoke(classLoader, ClassLoader.class, "findLoadedClass", new Class<?>[] { String.class }, className);
	}

	/** 强制调用 **/
	protected static Object invoke(Object obj, Class<?> clazz, String funcName, Class<?>[] paramTypes, Object... args) {
		try {
			Method method = clazz.getDeclaredMethod(funcName, paramTypes);
			method.setAccessible(true);
			return method.invoke(obj, args);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/** 类加载过滤 **/
	public interface IClassFilter {
		/**
		 * 检测过滤, 过滤则交由父类加载.
		 * 
		 * @return 返回ture则本加载器加载, 否则交由父类加载.
		 **/
		boolean filter(ScopeClassLoader loader, String className);
	}

	/** 通用过滤器(过滤父类加载过的类) **/
	public static class ParentClassFilter implements IClassFilter {
		@Override
		public boolean filter(ScopeClassLoader loader, String className) {
			// 检测父类是否加载过
			Class<?> pclass = findLoadedClassByLoader(loader.getParent(), className);
			if (pclass != null) {
				return false;
			}
			// 保留自身加载
			return true;
		}

		/** 判断加载器是否加载过类 **/
		protected Class<?> findLoadedClassByLoader(ClassLoader classLoader, String className) {
			return ScopeClassLoader.findLoadedClassByLoader(classLoader, className);
		}
	}

	/** 通用过滤器(全部自己重新加载一份) **/
	public static class ClassFilter implements IClassFilter {
		@Override
		public boolean filter(ScopeClassLoader loader, String className) {
			return true; // 保留自身加载
		}

		/** 是否属于这些包名中的1个 **/
		public static boolean checkIsPackage(String className, String[] packages) {
			// 包名过滤
			for (String filterPackage : packages) {
				if (className.indexOf(filterPackage) == 0) {
					return true;
				}
			}
			return false;
		}

		/** 检测是否属于这些类中的1个 **/
		public static boolean checkIsClass(String className, Class<?>[] classes) {
			for (Class<?> filterClass : classes) {
				if (className.equals(filterClass.getName())) {
					return true; // 相同
				}
			}
			return false;
		}
	}
}
